package com.soup.hummingbird.module.method.probe;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.alibaba.fastjson.JSON;
import com.soup.hummingbird.module.method.MethodTag;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReferenceArray;

/**
 * 〈一句话功能简述〉<br>
 *
 * @author zhaoyi
 * @date 2021-04-04 17:14
 * @see [相关类/方法]（可选）
 * @since [产品/模块版本] （可选）
 */
@Slf4j
public class ProfilingAspect {
    public static final int MAX_NUM = 1024 * 32;

    private final static AtomicInteger index = new AtomicInteger(0);
    private final static AtomicReferenceArray<MethodTag> methodTagArr = new AtomicReferenceArray<>(MAX_NUM);
    private final static Map<Integer, List<String>> methodParameterGroup = new ConcurrentHashMap<>();

    /**
     * 生成插桩方法的id，用于标记方法
     * 这里的生成的策略是 atomic int 递增
     */
    public static int generateMethodId(MethodTag tag) {
        int methodId = index.getAndIncrement();
        if (methodId > MAX_NUM) {
            return -1;
        }
        methodTagArr.set(methodId, tag);
        return methodId;
    }

    /**
     * 将方法的入参信息添加到集合中
     */
    public static synchronized void setMethodParameterGroup(final int methodId, String parameterName) {
        List<String> parameterList = methodParameterGroup.computeIfAbsent(methodId, k -> new ArrayList<>());
        parameterList.add(parameterName);
    }

    /**
     * 方法调用完成，输出相关信息
     */
    public static void point(final long startNanos, final int methodId, Object[] requests, Object response) {
        MethodTag method = methodTagArr.get(methodId);
        List<String> parameters = methodParameterGroup.get(methodId);
        // System.out.println("监控 - Begin");
        // System.out.println("方法：" + method.getFullClassName() + "." + method.getMethodName());
        // System.out.println("入参：" + JSON.toJSONString(parameters) + " 入参类型：" + JSON.toJSONString(method.getParameterTypeList()) + " 入数[值]：" + JSON.toJSONString(requests));
        // System.out.println("出参：" + method.getReturnParameterType() + " 出参[值]：" + JSON.toJSONString(response));
        // System.out.println("耗时：" + (System.nanoTime() - startNanos) / 1000000 + "(ms)");
        // System.out.println("监控 - End\r\n");

        log.info("监控-begin");
        log.info("方法名：{}.{}", method.getFullClassName(), method.getMethodName());
        log.info("入参信息：入参名{}，入参类型：{}，入参值信息：{}", JSON.toJSONString(parameters), JSON.toJSONString(method.getParameterTypeList()), JSON.toJSONString(requests));
        log.info("出参数信息：出参类型{}，出参值信息：{}", method.getReturnParameterType(), JSON.toJSONString(response));
        log.info("方法执行耗时：{}(ms)", (System.nanoTime() - startNanos) / 1000000);
        log.info("监控-end");
    }

    /**
     * 方法调用发生异常，输出相关信息
     */
    public static void point(final long startNanos, final int methodId, Object[] requests, Throwable throwable) {
        MethodTag method = methodTagArr.get(methodId);
        List<String> parameters = methodParameterGroup.get(methodId);
        // System.out.println("监控 - Begin");
        // System.out.println("方法：" + method.getFullClassName() + "." + method.getMethodName());
        // System.out.println("入参：" + JSON.toJSONString(parameters) + " 入参类型：" + JSON.toJSONString(method.getParameterTypeList()) + " 入数[值]：" + JSON.toJSONString(requests));
        // System.out.println("异常：" + throwable.getMessage());
        // System.out.println("耗时：" + (System.nanoTime() - startNanos) / 1000000 + "(ms)");
        // System.out.println("监控 - End\r\n");

        log.info("监控-begin");
        log.info("方法名：{}.{}", method.getFullClassName(), method.getMethodName());
        log.info("入参信息：入参名{}，入参类型：{}，入参值信息：{}", JSON.toJSONString(parameters), JSON.toJSONString(method.getParameterTypeList()), JSON.toJSONString(requests));
        log.info("异常信息：{}，堆栈信息：{}", throwable.getMessage(), ExceptionUtil.stacktraceToOneLineString(throwable));
        log.info("方法执行耗时：{}(ms)", (System.nanoTime() - startNanos) / 1000000);
        log.info("监控-end");
    }

    /**
     * 无法返回值的打印
     * @param startNanos 开始时间
     * @param methodId 方法id
     * @param requests 请求
     */
    public static void point(final long startNanos, final int methodId, Object[] requests) {
        MethodTag method = methodTagArr.get(methodId);
        List<String> parameters = methodParameterGroup.get(methodId);
        // System.out.println("监控 - Begin");
        // System.out.println("方法：" + method.getFullClassName() + "." + method.getMethodName());
        // System.out.println("耗时：" + (System.nanoTime() - startNanos) / 1000000 + "(ms)");
        // System.out.println("监控 - End\r\n");

        log.info("监控-begin");
        log.info("方法名：{}.{}", method.getFullClassName(), method.getMethodName());
        log.info("入参信息：入参名{}，入参类型：{}，入参值信息：{}", JSON.toJSONString(parameters), JSON.toJSONString(method.getParameterTypeList()), JSON.toJSONString(requests));
        log.info("方法执行耗时：{}(ms)", (System.nanoTime() - startNanos) / 1000000);
        log.info("监控-end");
    }

}
